/************************************************
 *** Datei: DroneSwarmController.cpp
 ***
 *** Erstellt am: 17.05.2012
 ***       Autor: Max Stark <Max.Stark88@googlemail.com>
 ***	 Version: 1.0
 ***	
 *** Beschreibung:
 *** <hier Beschreibung einfuegen>
 ************************************************/

#include "DroneSwarmController.h"

DroneSwarmController::DroneSwarmController() {
	this->init();
}

DroneSwarmController::DroneSwarmController(IDrone* drone) {
	this->drone = drone;
	this->init();
}

void DroneSwarmController::init() {
	this->droneNetController = new DroneNetController(this);
	this->droneList = new vector<DroneListEntry>();
	this->droneController = new DroneController();
	this->drone->addDroneController(this->droneController);
	this->uiController = new UIController(this);
}

/*
void DroneSwarmController::setDrone(IDrone* drone) {
	this->drone = drone;
	//this->setDroneControlMode(MANUELL);
}

void DroneSwarmController::setDroneControlMode(DroneControlMode droneControlMode) {
	if(droneControlMode == MANUELL) {
		this->drone->setDroneController(NULL);
	} else {
		this->drone->setDroneController(this->droneController);
	}
	this->droneControlMode = droneControlMode;
}
*/




/*
 * Prueft, ob eine Drohne bereits in der Drohnen-Liste vorhanden
 * ist. Falls ja, wird die TTL erneuert und geprueft ob sich das
 * Modell der Drohne geaendert hat.
 * War die Drohne vorher nicht in der Liste, wird sie nun aufgenommen
 */
void DroneSwarmController::insertIntoDroneList(DroneListEntry drone) {
	// Zugang zur Liste sperren
	this->droneListMutex.lock();

	bool triggerUpdate = false;
	bool exists = false;
	// Ist Drohne schon in der Liste?
	for(size_t i=0; i<this->droneList->size(); ++i) {
		if(this->droneList->at(i).strIPAddress == drone.strIPAddress) {
			// TTL auffrischen
			this->droneList->at(i).TTL = DRONELIST_MAX_TTL;
			// Hat sich das DronenModell geandert?
			if(this->droneList->at(i).strDroneModelName != drone.strDroneModelName) {
				this->droneList->at(i).strDroneModelName = drone.strDroneModelName;
				// UI informieren
				triggerUpdate = true;
			}
			exists = true;
		}
	}
	// Wenn Drohne nicht in Liste gefunden wurde: einfuegen
	if(!exists) {
		drone.TTL = DRONELIST_MAX_TTL;
		drone.masterDrone = false;
		drone.slaveDrone = false;
		this->droneList->push_back(drone);
		triggerUpdate = true;
	}

	// Liste wieder freigeben
	this->droneListMutex.unlock();

	if(triggerUpdate == true)
		this->uiController->triggerUpdate(UPDATE_DRONELIST);
}

/*
 *
 */
void DroneSwarmController::updateDroneList_Slave(string ipAddress, bool slave) {
	// Zugang zur Liste sperren
	this->droneListMutex.lock();

	bool triggerUpdate = false;
	for(size_t i=0; i<this->droneList->size(); ++i) {
		if(this->droneList->at(i).strIPAddress == ipAddress) {
			// Wenn Drohne bereits Master ist, dann erst FIN senden
			if(slave == true && this->droneList->at(i).masterDrone == true) {
				this->droneListMutex.unlock();
				this->sendMasterMessage(i);
				this->droneListMutex.lock();
			}
			this->droneList->at(i).slaveDrone = slave;
			// UI informieren
			triggerUpdate = true;
			break;
		}
	}

	// Liste wieder freigeben
	this->droneListMutex.unlock();

	if(triggerUpdate == true)
		this->uiController->triggerUpdate(UPDATE_DRONELIST);
}

/*
 * Prueft in regelmaessigen Intervallen, ob die TTL einer Drohne
 * in der Drohnen-Liste abgelaufen ist. Falls ja wird die Drohne
 * aus der Liste entfernt, falls nein wird ihre TTL reduziert. Die
 * Auffrischung der TTL erfolgt immer dann, wenn erneut ein Drohnen-
 * Beacon der Drohne empfangen wurde
 */
void DroneSwarmController::checkDroneList() {
	while(this->checkDroneListThread_Active) {
		// Liste sperren
		this->droneListMutex.lock();

		bool triggerUpdate = false;
		for(size_t i=0; i<this->droneList->size(); ++i) {
			// Pruefen ob TTL abgelaufen
			if(this->droneList->at(i).TTL == 0) {
				// Drohnen-Eintrag loeschen
				vector<DroneListEntry>::iterator actElem = this->droneList->begin();
				vector<DroneListEntry>::iterator lastElem = this->droneList->end();
				for(; actElem!=lastElem; ++actElem) {
					//if(actElem.base() == &(this->droneList->at(i)))
						this->droneList->erase(actElem);
				}
				// UI benachrichtigen
				triggerUpdate = true;

			} else {
				--this->droneList->at(i).TTL;
			}
		}

		// Liste freigeben
		this->droneListMutex.unlock();

		if(triggerUpdate == true)
			this->uiController->triggerUpdate(UPDATE_DRONELIST);
		this_thread::sleep(boost::posix_time::milliseconds(DRONE_BEACON_INTERVAL));
	}
}

/*
 * Die gewaehlte Drohne (anhand des Index in der Liste) wird zum Master gesetzt oder
 * zurueckgesetzt. Alle anderen Drohnen bekommen den bekommen den Master-Zustand
 * 'false'; damit ist sichergestellt dass es immer nur einen Master pro Drohne
 * gibt.
 */
void DroneSwarmController::sendMasterMessage(int index) {
	// Liste sperren
	this->droneListMutex.lock();

	// Nur wenn die gewaehlte Drohne kein Slave ist
	if(this->droneList->at(index).slaveDrone == false) {
		bool set = true;
		if(this->droneList->at(index).masterDrone == true)
			set = false;
		string ip = this->droneList->at(index).strIPAddress;

		this->droneListMutex.unlock();

		if(set == true) {
			this->actMasterDroneIPAddress = ip;
			this->getDrone()->setDroneController(this->droneController);
			this->droneNetController->sendDroneMasterMessage(ip, DRONE_MASTER_MES_REQ);
			//cout << "mastermessage REQ" << endl;
		} else {
			this->actMasterDroneIPAddress = "NO_MASTER";
			this->getDrone()->setDroneController(NULL);
			this->droneNetController->sendDroneMasterMessage(ip, DRONE_MASTER_MES_FIN);
			//cout << "mastermessage FIN" << endl;
		}

		this->droneListMutex.lock();

		for(size_t i=0; i<this->droneList->size(); ++i) {
			if(this->droneList->at(i).strIPAddress == this->droneList->at(index).strIPAddress)
				this->droneList->at(index).masterDrone = set;
			else
				this->droneList->at(i).masterDrone = false;
		}
	}

	this->droneListMutex.unlock();
	this->uiController->triggerUpdate(UPDATE_DRONELIST);
}

/*
 * Erstellt eine Kopie der DroneList und liefert sie zurueck
 */
vector<DroneListEntry>* DroneSwarmController::getDroneList() {
	this->droneListMutex.lock();

	// Liste kopieren
	vector<DroneListEntry>* tmp = new vector<DroneListEntry>();
	for(size_t i=0; i<this->droneList->size(); ++i) {
		DroneListEntry drone;
		drone.TTL = this->droneList->at(i).TTL;
		drone.masterDrone = this->droneList->at(i).masterDrone;
		drone.slaveDrone = this->droneList->at(i).slaveDrone;
		drone.strDroneHostName = this->droneList->at(i).strDroneHostName;
		drone.strDroneModelName = this->droneList->at(i).strDroneModelName;
		drone.strIPAddress = this->droneList->at(i).strIPAddress;

		tmp->push_back(drone);
	}

	this->droneListMutex.unlock();
	return tmp;
}




void DroneSwarmController::run() {
	this->checkDroneListThread_Active = true;
	this->checkDroneListThread = new thread(
			&DroneSwarmController::checkDroneList, this);

	//if(this->drone != NULL)
		//drone->connectDrone();
	this->droneNetController->run();

	this->isRunning = true;
}

void DroneSwarmController::stop() {

	// An die Slave ein FIN senden
	for(size_t i=0; i<this->droneList->size(); ++i) {
		if(this->droneList->at(i).slaveDrone == true)
			this->sendMasterMessage(i);
	}

	if(this->isRunning == true) {
		this->droneNetController->stop();

		this->checkDroneListThread_Active = false;
		this->checkDroneListThread->join();
		//cout << "checkDroneListThread terminiert" << endl;

		delete this->checkDroneListThread;
		delete this->droneList;
		delete this->droneController;
		//delete this->uiController;
		delete this->droneNetController;

		this->isRunning = false;
	}
}

DroneSwarmController::~DroneSwarmController() {
	this->stop();
}

